package com.sunday.redisson.lock;

import com.sunday.redisson.annotation.OpenDistributedLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * org.springframework.amqp.rabbit.annotation.RabbitListenerAnnotationBeanPostProcessor
 * Ordered ->
 * 是一个接口，它定义了一个单一方法 getOrder()，用于获取对象的顺序。
 * 实现该接口的对象可以被排序，并且在应用程序中按照特定的顺序进行处理。
 * 此接口通常与 Spring 框架中的拦截器、过滤器、监听器等组件一起使用，以确保它们按正确的顺序执行。
 * 具体来说，在相同的阶段（如初始化或销毁）中有多个对象需要顺序执行时，可以使用 Ordered 接口对它们进行排序并按顺序执行。
 * <p>
 * BeanPostProcessor -> 是一个 Spring 框架中的接口，其主要作用是在 Spring 容器实例化、配置和初始化 Bean 对象之后，
 * 在调用 Bean 的初始化方法前后对 Bean 进行一些额外的处理操作，例如修改属性值、添加代理对象等。
 * 它为开发者提供了在 Bean 初始化过程中干预的机会，可以通过实现该接口，自定义对 Bean 的处理逻辑，从而满足特定的需求。
 * <p>
 * BeanFactoryAware ->
 * 是 Spring 框架中的一个接口，实现该接口的 Bean 可以获取它们所属的 Bean 工厂（BeanFactory）对象。
 * 通过实现该接口，Bean 可以在需要时访问其所属的 BeanFactory 对象，例如在运行时从容器中检索其他 Bean 实例或请求服务。
 * 需要注意的是，自 Spring 3.1 版本起，推荐使用更通用的 ApplicationContextAware 接口来替代 BeanFactoryAware，
 * 因为 ApplicationContext 继承了 BeanFactory，并提供了更多的功能和更多的应用场景。
 * <p>
 * BeanClassLoaderAware ->
 * 是 Spring Framework 中的一个接口，用于在 bean 实例化时将其类加载器传递给该 bean。通过实现这个接口，
 * 可以让 bean 对象在运行时获取自己的类加载器，并使用它来加载其他类或资源。
 * 这通常用于需要动态载入类的场景，例如插件系统、类似于 Servlet 容器的 Web 应用程序容器等。
 * <p>
 * EnvironmentAware ->
 * 是 Spring 框架中的一个接口，用于获取和操作 Spring 应用程序上下文的环境变量。
 * 通过实现该接口并重写其 "setEnvironment" 方法，可以在应用程序启动时访问和修改环境变量。
 * <p>
 * SmartInitializingSingleton ->
 * 是一个接口，它继承了 InitializingBean 接口，用于在 Spring Bean 实例化后完成自定义初始化操作。
 * 与 InitializingBean 不同的是，SmartInitializingSingleton 的 initialize() 方法将在所有单例 Bean 实例化之后被调用，
 * 因此它可以利用其他单例 Bean 的状态和属性来执行更复杂的初始化逻辑。
 */
@Slf4j
//@Component
@Deprecated
public class OpenDistributedLockAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered {
//        BeanFactoryAware,
//        BeanClassLoaderAware, EnvironmentAware, SmartInitializingSingleton {

    private final ConcurrentMap<Class<?>, TypeMetadata> typeCache = new ConcurrentHashMap<>();

    /**
     * BeanPostProcessor
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//        log.info("Before Initialization -> {} , {}", bean, beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
        Class<?> targetClass = AopUtils.getTargetClass(bean);
        log.info("After Initialization -> {} , {}, {}", bean, beanName, targetClass);
        final TypeMetadata metadata = this.typeCache.computeIfAbsent(targetClass, this::buildMetadata);
        for (ListenerMethod lm : metadata.listenerMethods) {
            for (OpenDistributedLock rabbitListener : lm.annotations) {
                log.info("================================" + rabbitListener);
            }
        }
        return bean;
    }


    private TypeMetadata buildMetadata(Class<?> targetClass) {
        List<OpenDistributedLock> classLevelListeners = findListenerAnnotations(targetClass);
        final boolean hasClassLevelListeners = classLevelListeners.size() > 0;
        final List<ListenerMethod> methods = new ArrayList<>();
        final List<Method> multiMethods = new ArrayList<>();
        ReflectionUtils.doWithMethods(targetClass, method -> {
            List<OpenDistributedLock> listenerAnnotations = findListenerAnnotations(method);
            if (listenerAnnotations.size() > 0) {
                methods.add(new ListenerMethod(method,
                        listenerAnnotations.toArray(new OpenDistributedLock[listenerAnnotations.size()])));
            }
//            if (hasClassLevelListeners) {
//                RabbitHandler rabbitHandler = AnnotationUtils.findAnnotation(method, RabbitHandler.class);
//                if (rabbitHandler != null) {
//                    multiMethods.add(method);
//                }
//            }
        }, ReflectionUtils.USER_DECLARED_METHODS);
        if (methods.isEmpty() && multiMethods.isEmpty()) {
            return TypeMetadata.EMPTY;
        }
        return new TypeMetadata(
                methods.toArray(new ListenerMethod[methods.size()]),
                multiMethods.toArray(new Method[multiMethods.size()]),
                classLevelListeners.toArray(new OpenDistributedLock[classLevelListeners.size()]));
    }


    private List<OpenDistributedLock> findListenerAnnotations(AnnotatedElement element) {
        return MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY)
                .stream(OpenDistributedLock.class)
                .map(ann -> ann.synthesize())
                .toList();
    }

    /**
     * Ordered
     */
    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }


    private static class TypeMetadata {
        final ListenerMethod[] listenerMethods;
        final Method[] handlerMethods;
        final OpenDistributedLock[] classAnnotations;

        static final TypeMetadata EMPTY = new TypeMetadata();

        private TypeMetadata() {
            this.listenerMethods = new ListenerMethod[0];
            this.handlerMethods = new Method[0];
            this.classAnnotations = new OpenDistributedLock[0];
        }

        TypeMetadata(ListenerMethod[] methods, Method[] multiMethods, OpenDistributedLock[] classLevelListeners) { // NOSONAR
            this.listenerMethods = methods;
            this.handlerMethods = multiMethods;
            this.classAnnotations = classLevelListeners;
        }

    }

    /**
     * A method annotated with {@link OpenDistributedLock}, together with the annotations.
     *
     * @param method
     * @param annotations
     */
    private record ListenerMethod(Method method, OpenDistributedLock[] annotations) {
    }

}
